#ifndef __GRILLA_H__
#define __GRILLA_H__

#include "malloc.h"
#include "Imprimible.h"
#include "Casillero.h"
#include "Obstaculo.h"
#include "PositionOutOfBoundException.h"
#include <iostream>

using namespace std;

class Grilla: public Imprimible {

	private:
		int filas;
		int columnas;
		std::string tipoObstaculoPorDefecto;
		Casillero** casilleros;

		void initilize(int filas, int columnas, std::string obsDefecto){
			this->filas = filas;
			this->columnas = columnas;

			this->casilleros = (Casillero**)malloc(filas*sizeof(Casillero*));

			for(int i=0;i<filas; i++){
				this->casilleros[i] = (Casillero*)malloc(columnas*sizeof(Casillero));
			}

			this->tipoObstaculoPorDefecto = obsDefecto;
			for(int i=0;i<this->filas;i++){
				for(int j=0;j<this->columnas;j++){
					this->casilleros[i][j].setPosicion(i,j);
					Casillero* casillero = &this->casilleros[i][j];
					Obstaculo* obs = new Obstaculo(i,j);
					obs->setTipo(this->tipoObstaculoPorDefecto);
					// NO HACE FALTA MAS, LO DEJO HASTA TERMINAR PRUEBAS
					//obs->setTextura('x');
					casillero->setContenido(obs);
					this->casilleros[i][j] = (*casillero);
				}
			}
		}

	public:

		Grilla(int filas, int columnas, std::string obsDefecto){
			this->initilize(filas,columnas,obsDefecto);

		}

		Grilla(int filas, int columnas){
			this->initilize(filas,columnas,"desconocido");
		}

		int getFilas(){
			
			return this->filas;
		}

		int getColumnas(){
		
			return this->columnas;
		}

		string getTipoObstaculoPorDefecto(){
			return this->tipoObstaculoPorDefecto;
		}

		Casillero* getCasillero(int fila, int columna){
			
			if(this->esPosicionInvalida(fila,columna)) {
				throw new PositionOutOfBoundException();
			} else {

				return &this->casilleros[fila][columna];
			}
			
		}

		void setCasillero(int fila, int columna, Casillero* casillero){
			
			if(this->esCasilleroInvalido(casillero)) {
				throw new PositionOutOfBoundException();
			} else {
				this->casilleros[fila][columna] = (*casillero);
			}
		}

		bool Grilla::hayCasilleroArriba(Casillero* casillero){

			if(casillero->getFila() != 0)
				return true;
			return false;
		}

		bool Grilla::hayCasilleroAbajo(Casillero* casillero){

			if(casillero->getFila() < this->filas - 1)
				return true;
			return false;
		}

		bool Grilla::hayCasilleroALaDerecha(Casillero* casillero){

			if(casillero->getColumna() < this->columnas -1)
				return true;
			return false;
		}

		bool Grilla::hayCasilleroALaIzquierda(Casillero* casillero){

			if(casillero->getColumna() != 0)
				return true;
			return false;
		}

		bool esCasilleroInvalido(Casillero* casillero){
		
			if( (casillero->getFila() < 0) || (casillero->getFila() >= this->filas) ){
			
				return true;
			}
			
			if( (casillero->getColumna()< 0) || (casillero->getColumna() >= this->columnas) ){

				return true;
			}
			return false;
		}

		bool esPosicionInvalida(int fila, int columna){
		
			if( (fila < 0) || (fila >= this->filas) ){

				return true;
			}

			if( (columna < 0) || (columna >= this->columnas) ){

				return true;
			}
			return false;
		}
		
		void Grilla::imprimir(){

			bool cambieVertical = false;
			bool cambieHorizontal = false;
			bool imprimi = false;

			for(int i=0;i<this->filas;i++){
				for(int j=0;j<this->columnas;j++){

					Casillero casillero = this->casilleros[i][j];
					Contenido* contenido = casillero.getContenido();

					if(contenido->esCamino()){

						if(this->hayCasilleroArriba(&casillero)){

							Contenido* contenidoArriba = this->casilleros[casillero.getFila()-1][j].getContenido();
							if(contenidoArriba->esCamino()){
								casillero.getContenido()->setTextura('|');
								cambieVertical = true;
							}
						}

						if(this->hayCasilleroAbajo(&casillero)){

							Contenido* contenidoAbajo = this->casilleros[casillero.getFila()+1][j].getContenido();
							if(contenidoAbajo->esCamino()){
								casillero.getContenido()->setTextura('|');
								cambieVertical = true;
							}
						}

						if(this->hayCasilleroALaDerecha(&casillero)){

							Contenido* contenidoDerecha = this->casilleros[i][casillero.getColumna()+1].getContenido();
							if(contenidoDerecha->esCamino()){
								casillero.getContenido()->setTextura('-');
								cambieHorizontal = true;
							}
						}

						if(this->hayCasilleroALaIzquierda(&casillero)){

							Contenido* contenidoIzquierda = this->casilleros[i][casillero.getColumna()-1].getContenido();
							if(contenidoIzquierda->esCamino()){
								casillero.getContenido()->setTextura('-');
								cambieHorizontal = true;
							}
						}

						if(cambieVertical && cambieHorizontal){
							casillero.getContenido()->setTextura('+');
						}

						cambieVertical = false;
						cambieHorizontal = false;

					}else{

						casillero.imprimir();
						imprimi = true;
					}
					if(!imprimi)
						casillero.imprimir();
					imprimi=false;

				}
				fprintf(stdout,"\n");
			}
		}

		void imprimir2(){
			
			for(int i = 0; i < this->filas; i++){
				
				for(int j = 0; j < this->columnas; j++){
					
					this->getCasillero(i,j)->imprimir2();
					
				}
				cout<<endl;
			}
	
		}

		~Grilla(){

			for(int i=0;i<this->filas;i++){
				for(int j=0;j<this->columnas;j++){
					delete this->casilleros[i][j].getContenido();
				}
			}

			for(int i=0;i<this->filas;i++){
				free(this->casilleros[i]);
			}
			free(this->casilleros);
		}
};
#endif 
